//! 序列文件实现
//!
//! 该模块实现参考 linux `seq_file`, 可以将格式化的数据通过文件形式读取,
//! 可以方便的向其他程序提供可视化的数据交互.

use std::{any::Any, sync::Arc};

use crate::{
    inode::{DfsInode, FileCreateOptions, InodeOperation},
    DfsError, DfsResult,
};

/// seq file 内部管理结构
///
/// 提供给 `seq_println` 和 `seq_print` 使用,
/// *注意*: 创建者不用使用.
pub struct SeqFile {
    /// aaa
    pub buf: String,
    /// aaa
    pub read_count: u64,
    read_pos: u64,
}

impl SeqFile {
    fn new() -> Self {
        Self { buf: String::new(), read_count: 0, read_pos: 0 }
    }
}

/// seq file operation trait
///
/// 注册为 seq file 的节点实现该 trait
#[allow(unused_variables)]
pub trait SeqFileOperation {
    /// 预备开始执行 seq, 初始化并预备数据
    ///
    /// # Errors
    /// 由实现者返回错误
    fn start(&self, m: &mut SeqFile) -> DfsResult<Box<dyn Any + Send>> {
        let empty = Box::new(());
        Ok(empty)
    }
    /// 停止 seq
    fn stop(&self, m: &mut SeqFile, v: &mut Box<dyn Any + Send>) {}
    /// 显示数据(使用 `seq_println/seq_print`)
    ///
    /// # Errors
    /// 由实现者返回错误
    fn show(&self, m: &mut SeqFile, v: &mut Box<dyn Any + Send>) -> DfsResult<()>;
    /// 向 seq file 写入数据
    ///
    /// # Errors
    /// 由实现者返回错误
    fn write(&self, buf: &str, pos: &mut u64) -> DfsResult<usize> {
        Ok(0)
    }
    /// 默认返回 0
    ///
    /// 如果对应实现可以快速获取 size, 则直接返回 size,
    /// 否则如果返回 0, 则需要通过 traverse 来慢速获取 size.
    fn size(&self) -> usize {
        0
    }
}

struct InnerSeqFile {
    inner: Arc<dyn SeqFileOperation>,
}

unsafe impl Send for InnerSeqFile {}
unsafe impl Sync for InnerSeqFile {}

impl InnerSeqFile {
    fn new(seq_file: Arc<dyn SeqFileOperation>) -> Self {
        Self { inner: seq_file }
    }
}

/// seq 文件创建选项
#[derive(Debug, Clone, Default)]
pub struct SeqFileCreateOptions {
    read: bool,
    write: bool,
    name: String,
}

impl SeqFileCreateOptions {
    /// 创建一个新的 `SeqFileCreateOptions`
    pub fn new() -> Self {
        Self::default()
    }

    /// 节点是否可读
    pub fn read(&mut self, read: bool) -> &mut Self {
        self.read = read;
        self
    }

    /// 节点是否可写
    pub fn write(&mut self, write: bool) -> &mut Self {
        self.write = write;
        self
    }

    /// 节点是否可读可写
    pub fn read_write(&mut self, rdwr: bool) -> &mut Self {
        self.read(rdwr);
        self.write(rdwr);
        self
    }
    /// 节点名字
    pub fn name(&mut self, name: &str) -> &mut Self {
        self.name = name.to_string();
        self
    }

    /// 创建 seq file 文件
    ///
    /// # Errors
    /// 节点为文件, 创建属性, 格式不匹配等将返回错误
    pub fn create_seq_file(
        &self,
        inode: Arc<DfsInode>,
        handler: Arc<dyn SeqFileOperation>,
    ) -> DfsResult<()> {
        let seq_file = Arc::new(InnerSeqFile::new(handler));
        FileCreateOptions::new()
            .read(self.read)
            .write(self.write)
            .seek(true)
            .name(&self.name)
            .create_file(inode, seq_file)?;
        Ok(())
    }
}

fn traverse(inner: &InnerSeqFile, seq_file: &mut SeqFile) -> DfsResult<()> {
    if !seq_file.buf.is_empty() {
        return Ok(());
    }

    let mut p2 = inner.inner.start(seq_file)?;
    let ret = inner.inner.show(seq_file, &mut p2);
    inner.inner.stop(seq_file, &mut p2);
    ret
}

impl InodeOperation for InnerSeqFile {
    fn open(&self) -> DfsResult<Box<dyn Any + Send>> {
        let seq_file = Box::new(SeqFile::new());
        Ok(seq_file)
    }

    fn read(
        &self,
        data: &mut Box<dyn Any + Send>,
        buf: &mut String,
        pos: &mut u64,
    ) -> DfsResult<usize> {
        let seq_file: &mut SeqFile = data.downcast_mut().unwrap();

        traverse(self, seq_file)?;

        if *pos != seq_file.read_pos {
            seq_file.read_pos = *pos;
        }

        if seq_file.read_count == 0 {
            return Ok(0);
        }

        let total;
        let slice = seq_file.buf.as_str();
        let cap = buf.capacity();
        if cap == 0 {
            let str = &slice[seq_file.read_pos as usize..];
            buf.push_str(str);
            seq_file.read_count = 0;
            total = str.len();
        } else {
            let size = seq_file.read_count.min(cap as u64);
            let str =
                &slice[seq_file.read_pos as usize..seq_file.read_pos as usize + size as usize];
            buf.push_str(str);
            total = str.len();
            seq_file.read_count -= size;
            seq_file.read_pos += size;
        }
        *pos += total as u64;

        Ok(total)
    }

    fn write(&self, _: &mut Box<dyn Any + Send>, buf: &str, pos: &mut u64) -> DfsResult<usize> {
        self.inner.write(buf, pos)
    }

    fn seek(
        &self,
        data: &mut Box<dyn Any + Send>,
        pos: crate::DSeekFrom,
        raw_pos: &mut u64,
    ) -> DfsResult<u64> {
        let seq_file: &mut SeqFile = data.downcast_mut().unwrap();
        traverse(self, seq_file)?;
        let max_size = seq_file.buf.len() as u64;

        let new_pos = match pos {
            crate::DSeekFrom::Start(s) => s,
            crate::DSeekFrom::Current(cur) => raw_pos.wrapping_add_signed(cur),
            crate::DSeekFrom::End(end) => max_size.wrapping_add_signed(end),
        };

        if new_pos > max_size {
            return Err(DfsError::IllegalSeek);
        }

        if new_pos < seq_file.read_pos {
            let off = seq_file.read_pos - new_pos;
            seq_file.read_count += off;
        } else {
            let off = new_pos - seq_file.read_pos;
            seq_file.read_count -= off;
        }

        seq_file.read_pos = new_pos;

        *raw_pos = new_pos;

        Ok(new_pos)
    }

    fn size(&self) -> usize {
        let size = self.inner.size();
        if size == 0 {
            let mut seq_file = Box::new(SeqFile::new());
            let res = traverse(self, &mut seq_file);
            if res.is_ok() { seq_file.buf.len() } else { 0 }
        } else {
            size
        }
    }
}

/// seq file show
///
/// 在 seq show 中记录输出
#[macro_export]
macro_rules! seq_print {
    ($ident:ident, $($arg:tt)*) => {
        let s = format!($($arg)*);
        $ident.buf.push_str(s.as_str());
        $ident.read_count += s.len() as u64;
    };
}

/// seq file show
///
/// 在 seq show 中记录输出自动附加换行符
#[macro_export]
macro_rules! seq_println {
    ($ident:ident) => {
        $crate::seq_print!($ident, "\n");
    };

    ($ident:ident, $($arg:tt)*) => {
        $crate::seq_print!($ident, "{}\n", format_args!($($arg)*));
    };
}
